#ifndef LISTA_H
#define LISTA_H

#include <iostream>
#include "Nodo.h"

using namespace std;

template< class T >
class Lista {

public:
	Lista();
	~Lista();
	Lista(const Lista< T > &otra);                     
	void insertarNodoIndice( const int &, const T & );        
	void modificarNodoIndice ( const int &, const T & );
	void borrarNodoIndice( const int & );
	bool hayIndice( const int & ) const;
	int tamanio() const;
   	bool operator==(const Lista<T> &otra) const;
	const T& dameValor( const int & c) const;
	void imprimir() const;
	
private:
	Nodo<T> *primero, *ultimo;
	int size;
	
	// estas funciones son privadas para evitar que dentro de Rala cometamos algun error con las listas.
	void comienzo();
	void insertarAlFinal(const int &, const T &);
	Nodo< T > *dameNodoIndice( const int & );     // me da el puntero que apunta al nodo indice n
};

template< class T >
Lista< T >::Lista() { 
	size = 0;
	primero=NULL;
	ultimo=NULL;

}

template< class T>
Lista< T >::~Lista() {
	Nodo< T > *actual;

    while ( size > 0 ) {
	  actual = primero;	  
          primero = primero -> proximo;
          delete actual;
          size--;
    }
	
}

template< class T >
Lista< T >::Lista( const Lista< T > &otra){
       size = 0;
       
       if ( otra.tamanio() == 0 ) {
            primero = NULL;
            ultimo = NULL;
       }
       else {
            Nodo< T > *actual;
            actual = otra.primero;
            while ( actual != NULL ) {
                  this->insertarAlFinal(actual->indice, actual->elemento);     
                  actual = actual->verSiguiente();
            }
       }
}
             

template< class T >
void Lista< T >::insertarAlFinal( const int &i, const T &elemento){ 
	Nodo< T > *nuevoNodo;
	nuevoNodo = new Nodo< T >(i, elemento);
	if ( size == 0 )
	{
		primero = nuevoNodo;

	}
	else 
	{
		ultimo->proximo = nuevoNodo;
	
	}
	ultimo = nuevoNodo;
	size++;
}

template< class T >
void Lista< T >::comienzo(){ 

	if ( size == 0 ) cout << "No hay elementos a desencolar" << endl;
	else {
         	Nodo< T > *nodoOut = ultimo;
         	if ( primero == ultimo ) primero = ultimo = NULL;
       		else {
         		Nodo< T > *actual;
         		actual = primero;
			while ( actual->verSiguiente() != ultimo ){
               			actual = actual->proximo;
         		}       
         		ultimo = actual;     
         		actual->proximo = NULL;
	     	}	size--;
		delete nodoOut;
	}	
}

template< class T >
void Lista< T >::insertarNodoIndice( const int & c, const T & v){
              Nodo< T > *actual, *final;
              actual = primero;
              final = ultimo;                                    // conserva el ultimo nodo de la lista original
              
              if ( actual != NULL && actual->indice > c) {              // si el indice es menor que el indice del primer nodo, lo agrega adelante
                   Nodo< T > *agregarAdelante = new Nodo< T >(c,v);
                   primero = agregarAdelante;
                   primero->proximo = actual;
                   size++;
              } 
              else {                                                   // Si no se da ese caso, utiliza el *actual para recorrer la fila hasta el punto en el que
              while ( actual != NULL && actual->indice < c) {          // debe colocarse el nuevo nodo, y lo agrega al final de una "sublista" cambiando el 
                    ultimo = actual;            		       // puntero ultimo, que luego es restituido al final.
                    actual = actual->proximo;
              }
              this->insertarAlFinal(c,v);
              ultimo->proximo = actual;
              ultimo = final;
              }
}
     
template< class T >
void Lista< T >::modificarNodoIndice( const int & c, const T & v){
              dameNodoIndice(c)->cambiarValor(v);
}

template <class T>
const T& Lista< T >:: dameValor( const int & c) const {
	if ( !this->hayIndice(c) ) {
           cerr << "No existe un nodo en esa posicion";
      }
      else {
           Nodo< T > *actual;
           actual = primero;
           while ( actual != NULL && actual->indice != c) {
                 actual = actual->verSiguiente();
           }
           return actual->valor();    
      }

}

template< class T >
void Lista< T >::borrarNodoIndice( const int & c ){
               Nodo< T > *temp;
               if ( primero->indice == dameNodoIndice(c)->indice ) {          // si el indice es el del primero
                   temp = primero;
                   primero = primero->proximo;
                   delete temp;                               
                   size--;
               } 
               else {                                                  // en cualquier otro caso
                	temp = dameNodoIndice(c);
                	Nodo< T > *actual = primero;
                	while ( actual->verSiguiente() != dameNodoIndice(c) ){
               			actual = actual->proximo;
         		}
         		actual->proximo = dameNodoIndice(c)->proximo;
         		delete temp;
         		size--;
               }
}

template< class T >
bool Lista< T >::hayIndice( const int &n) const{
     Nodo< T > *nodoActual;
     nodoActual = primero;
     bool resultado;
     while ( nodoActual != NULL && nodoActual->indice != n ){
           nodoActual = nodoActual->verSiguiente();
     }
     resultado = nodoActual != NULL;
     return resultado;
}

template< class T >
int Lista< T >::tamanio() const {
      return size;
}

template< class T >
Nodo< T > *Lista< T >::dameNodoIndice( const int &n ){
      
      if ( !this->hayIndice(n) ) {
           cerr << "No existe un nodo en esa posicion";
      }
      else {
           Nodo< T > *actual;
           actual = primero;
           while ( actual != NULL && actual->indice != n) {
                 actual = actual->verSiguiente();
           }
           return actual;    
      }
} 

template< class T >  
bool Lista< T >::operator==(const Lista<T> &otra) const {
     
     if ( size != otra.size ) { 
          return false;
     }
     else {
          int i = 0;
          Nodo< T > *actual, *actualOtra;
          actual = primero;
          actualOtra = otra.primero;
          while ( actual != NULL && actualOtra != NULL && *actual == *actualOtra ){
                actual = actual->verSiguiente();
                actualOtra = actualOtra->verSiguiente();
                i++;
           }
           delete actual, actualOtra;
           return ( i == size );
     }
}

template< class T >
void Lista< T >::imprimir() const {
	if ( size == 0 ) cout << "Lista vacia." << endl;
	else {
		Nodo< T > *actual;
		actual = primero;
		while ( actual != NULL ){
			cout << "(" << actual->indice << "," << actual->elemento << ") ";
			actual = actual->proximo;
		}
		cout << endl;	
	}
}

#endif



